#include "Client.hpp"
#include "RemoteHandle.hpp"
#include "WebMonitor.hpp"
#include <fstream>
#include <filesystem>
#include <unistd.h>
#include <spdlog/spdlog.h>
#include "Config.h"
#if IPC_THRIFT_METHOD
#include "ThriftClient.hpp"
#else
#include "OldClientIPC.hpp"
#endif

#define CHECK_EXCEPTION(a) \
try \
{ \
  a \
} \
catch(const std::exception& e) \
{ \
  remoteHandle->SendMessage(e.what()); \
  return; \
}

#if IPC_THRIFT_METHOD
static ThriftClient* cameraPtr=nullptr;
#else
static OldClientIPC* cameraPtr=nullptr;
#endif
static WebMonitor* webMonitor=nullptr;
static RemoteHandle* remoteHandle=nullptr;

DeviceStatus deviceStatus;


//file data
std::string filePath; //最后一个字符为'/'
std::string ServerIP;
std::string ServerPort;
std::string DeviceName;

std::string fileName;

void ReadFileData()
{
  std::ifstream configFile;
  configFile.open("/etc/supermatrix/OldClient.conf");
  if(!configFile.is_open())
  {
    OATPP_LOGE("Config","Can't open config file,the default config file path:/etc/CameraCollege/config");
    throw;
  }
  configFile>>filePath>>ServerIP>>ServerPort>>DeviceName;
  if(!configFile.good())
  {
    OATPP_LOGE("Config","Can't Read Config File,please check file format");
    throw;
  }
  configFile.close();
  if(!std::filesystem::exists(filePath))
  {
    OATPP_LOGE("Config","Can't Open File Path");
    throw;
  }
  if(filePath.at(filePath.length()-1)!='/')
    filePath+='/';
}

Client::Client()
{
  deviceStatus=DeviceStatus::UnInit;
#if IPC_THRIFT_METHOD
  cameraPtr=new ThriftClient();
#else
  cameraPtr=new OldClientIPC();
#endif
  remoteHandle=new RemoteHandle(this);
  webMonitor=new WebMonitor(this,remoteHandle);
#if IPC_THRIFT_METHOD
  cameraPtr->Boot("127.0.0.1",14001);
#else
  cameraPtr->Boot();
#endif
  spdlog::info("Finding Camera");
  std::vector<std::string> list;
  do
  {
    list=cameraPtr->GetDeviceList();
    if(list.size()!=0)
      break;
    std::this_thread::sleep_for(std::chrono::seconds(3));
  }
  while(true);
  spdlog::info("Camera Finded");
  cameraPtr->SetSelectUUID(list.at(0));
  stream=new ImageStream(list.at(0));
}

Client::~Client()
{
  delete webMonitor;
  delete remoteHandle;
  delete cameraPtr;
  delete stream;
}

void Client::Run()
{
  ReadFileData();
  webMonitor->SetServerConfig(ServerIP,ServerPort,DeviceName);
  webMonitor->LoginServer();
}

void Client::sendErrorMessage(std::string msg)
{
  remoteHandle->SendMessage(fmt::format("错误:{}",msg));
}

void Client::sendPreviewImage(unsigned char* imageData,const int32_t imageHeight,const int32_t imageWidth,const pixelFormat imageFormat)
{
  remoteHandle->SetImage(imageData,imageHeight,imageWidth,imageFormat);
}

void Client::GetDeviceStatus()
{
  remoteHandle->SendDeviceStatus(static_cast<int32_t>(deviceStatus));
}

void Client::GetCameraParameter()
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CameraParament para={
      static_cast<int32_t>(cameraPtr->GetImageHeight()),
      static_cast<int32_t>(cameraPtr->GetImageWidth()),
      static_cast<int32_t>(cameraPtr->GetImageOffsetX()),
      static_cast<int32_t>(cameraPtr->GetImageOffsetY()),
      static_cast<int32_t>(cameraPtr->GetExposureTime()),
      static_cast<int>(cameraPtr->GetCollegeFrameRate()),
      cameraPtr->GetTriggerMode()? 0x1 : 0x0
    };
    remoteHandle->SendCameraPara(para);
  }
}

void Client::InitDevice()
{
  auto deviceList=cameraPtr->GetDeviceList();
  if(deviceList.size()==0)
  {
    deviceStatus=DeviceStatus::UnInit;
    return;
  }
  cameraPtr->SetSelectUUID(deviceList[0]);
  deviceStatus=DeviceStatus::Init;
}

void Client::StartShot()
{
  if(deviceStatus==DeviceStatus::Init)
  {
  CHECK_EXCEPTION(cameraPtr->StartCollegeImage();)
  deviceStatus=DeviceStatus::CameraShot;
  imgWidth=cameraPtr->GetImageWidth();
  imgHeight=cameraPtr->GetImageHeight();
  if(stream->GetSize()<imgWidth*imgHeight)
    throw std::logic_error("ImageStream size too small");
  ImgBuffer=new unsigned char[imgWidth*imgHeight];
  imagethread=std::jthread([this](std::stop_token token){
    while(!token.stop_requested())
    {
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    stream->GetRealtimeImg(ImgBuffer);
    sendPreviewImage(ImgBuffer,imgHeight,imgWidth,pixelFormat::MONO8);
    }
  });
  }
  deviceStatus=DeviceStatus::CameraShot;
}

void Client::StartSave()
{
  if(deviceStatus==DeviceStatus::CameraShot)
  {
    cameraPtr->SetImageSavePath(filePath.substr(0,filePath.length()-1));
    CHECK_EXCEPTION(fileName=cameraPtr->StartSaveImage();)
    deviceStatus=DeviceStatus::CmaeraSave;
  }
}

void Client::StopShot()
{
  if(deviceStatus==DeviceStatus::CameraShot)
  {
  CHECK_EXCEPTION(cameraPtr->StopCollegeImage();)
  deviceStatus=DeviceStatus::Init;
  if(imagethread.joinable())
  {
    imagethread.request_stop();
    imagethread.join();
    delete [] ImgBuffer;
  }
  }
}

void Client::StopSave()
{
  if(deviceStatus==DeviceStatus::CmaeraSave)
  {
    CHECK_EXCEPTION(cameraPtr->StopSaveImage();)
    deviceStatus=DeviceStatus::CameraShot;
  }
}

void Client::StartTrigger()
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetTriggerMode(true);)
  }
}

void Client::StopTrigger()
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetTriggerMode(false);)
  }
}

void Client::SetImageWidth(int32_t value)
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetImageWidth(value);)
  }
}

void Client::SetImageHeight(int32_t value)
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetImageHeight(value);)
  }
}

void Client::SetImageOffsetX(int32_t value)
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetImageOffsetX(value);)
  }
}

void Client::SetImageOffsetY(int32_t value)
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetImageOffsetY(value);)
  }
}

void Client::SetExposureTime(int32_t value)
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetExposureTime(value);)
  }
}

void Client::SetFrameRate(int32_t value)
{
  if(deviceStatus==DeviceStatus::Init)
  {
    CHECK_EXCEPTION(cameraPtr->SetImageFPSRate(value);)
  }
}

void Client::FileTranslate()
{
  remoteHandle->FileTransfer(filePath+fileName);
}
